package com.dsg.service.bank.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dsg.common.security.SystemAuthorizingUser;
import com.dsg.common.util.SingletonLoginUtils;
import com.dsg.common.util.UUIDGenerator;
import com.dsg.common.util.toolbox.CollectionUtil;
import com.dsg.common.util.toolbox.DateUtil;
import com.dsg.common.util.toolbox.StringUtil;
import com.dsg.dao.account.DsgAccountCardMapper;
import com.dsg.dao.bank.DsgBankDetailMapper;
import com.dsg.entity.account.DsgAccountCard;
import com.dsg.entity.account.DsgAccountTrade;
import com.dsg.entity.bank.DsgBankDetail;
import com.dsg.entity.bank.DsgBankRecord;
import com.dsg.entity.bank.DsgBankResult;
import com.dsg.entity.oil.DsgOilDataDetail;
import com.dsg.service.DsgServiceImpl;
import com.dsg.service.account.IDsgAccountCardService;
import com.dsg.service.account.IDsgAccountTradeService;
import com.dsg.service.bank.IDsgBankDetailService;
import com.dsg.service.bank.IDsgBankRecordService;
import com.dsg.service.bank.IDsgBankResultService;
import com.dsg.support.LeveldbAccess;
import com.dsg.util.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 銀行往來明細資料 服务实现类
 * </p>
 *
 * @author system
 * @since 2020-07-12
 */
@Service
public class DsgBankDetailServiceImpl extends DsgServiceImpl<DsgBankDetailMapper, DsgBankDetail> implements IDsgBankDetailService {

    @Autowired
    private DsgBankDetailMapper dsgBankDetailMapper;
    @Autowired
    private IDsgBankRecordService dsgBankRecordService;
    @Autowired
    private IDsgAccountTradeService dsgAccountTradeService;
    @Autowired
    private IDsgAccountCardService dsgAccountCardService;
    @Autowired
    private IDsgBankDetailService dsgBankDetailService;
    @Autowired
    private IDsgBankResultService dsgBankResultService;
    @Autowired
    private DsgAccountCardMapper dsgAccountCardMapper;
    @Autowired
    private LeveldbAccess leveldbAccess;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bankAccount(DsgAccountTrade dsgAccountTrade) {
        String dateType = dsgAccountTrade.getDateType();
        if (StringUtil.isNotEmpty(dateType)) {
            List<String> keyList = new ArrayList<>();
            try {
                String oid = UUIDGenerator.getUUID();
                switch (dateType) {
                    case "1":
                        if (StringUtil.isEmpty(dsgAccountTrade.getPayMode())) {
                            throw new RRException("上午過數檔案付款方式不能為空！");
                        }
                        break;
                    case "2":
                        if (StringUtil.isEmpty(dsgAccountTrade.getOilComs()) && StringUtil.isEmpty(dsgAccountTrade.getPayMode())) {
                            throw new RRException("下午過數檔案付款方式或油公司不能為空！");
                        }
                        break;
                }
                SystemAuthorizingUser sysUser = SingletonLoginUtils.getSystemUser();
                dsgAccountTrade.setEntCode(sysUser.getOrgCode());
                List<DsgAccountTrade> dsgAccountTradeList = dsgAccountTradeService.selectDsgAccountTrade(dsgAccountTrade);
                //繳款資料
                List<DsgBankDetail> dsgBankDetailList = new ArrayList<>();
                //交易賬目
                List<DsgAccountTrade> inDsgAccountTrade = new ArrayList<>();
                List<DsgAccountTrade> upDsgAccountTrade = new ArrayList<>();
                //
                List<DsgAccountCard> upDsgAccountCard = new ArrayList<>();
                //取油咭總賬并缓存
                QueryWrapper<DsgAccountCard> cardQueryWrapper = new QueryWrapper<>();
                cardQueryWrapper.eq("ENT_CODE", sysUser.getOrgCode());
                List<DsgAccountCard> dsgAccountCardList = dsgAccountCardMapper.selectList(cardQueryWrapper);
                dsgAccountCardList.forEach(I -> {
                    String key = leveldbAccess.genCacheKey(oid, DsgAccountCard.class.getSimpleName(), I.getCardNo());
                    leveldbAccess.put(key, I);
                    keyList.add(key);
                });
                dsgAccountCardList.clear();
                if (CollectionUtil.isNotEmpty(dsgAccountTradeList)) {
                    List<DsgBankDetail> saveDsgBankDetailList = new ArrayList<>();
                    List<DsgBankRecord> saveDsgBankRecordList = new ArrayList<>();
                    if ("2".equals(dateType)) {
                        Map<String, DsgAccountTrade> dsgAccountTradeMap = dsgAccountTradeList.stream().collect(Collectors.groupingBy(
                                I -> I.getBankCode(), Collectors.collectingAndThen(Collectors.reducing((c1, c2) -> c1.getTraAmount().compareTo(c2.getTraAmount()) > 0 ? c1 : c2), Optional::get)));
                        dsgAccountTradeList.clear();
                        upDsgAccountTrade.addAll(dsgAccountTradeMap.values());
                    }
                    //銀行往來明細資料分組
                    Map<String, List<DsgAccountTrade>> dsgAccountTradeMap = dsgAccountTradeList.stream().collect(Collectors.groupingBy(I -> I.getBankCode()));
                    //銀行往來明細資料
                    for (String tradeKey : dsgAccountTradeMap.keySet()) {
                        List<DsgAccountTrade> dsgAccountTrades = dsgAccountTradeMap.get(tradeKey);
                        BigDecimal sumTranValue = BigDecimal.ZERO;
                        String detailOid = UUIDGenerator.getUUID();
                        for (DsgAccountTrade dat : dsgAccountTrades) {
                            dat.setBankDetailId(detailOid);
                            upDsgAccountTrade.add(dat);
                            //初定銀行過數據成功登記交易賬目
                            DsgAccountTrade trade = new DsgAccountTrade();
                            BeanUtils.copyProperties(dat, trade);
                            trade.setOid(UUIDGenerator.getUUID());
                            trade.setTranItem(DsgAccountTrade.AppStatus.autopay.getCode());
                            trade.setInOut("IN");
                            trade.setBankDetailId(detailOid);
                            trade.setCreater(null);
                            trade.setCreateTime(null);
                            trade.setUpdater(null);
                            trade.setUpdateTime(null);
                            trade.setEntCode(null);
                            switch (dateType) {
                                case "1":
                                    trade.setPreDept(dat.getAftDept());
                                    trade.setTraAmount(dat.getAftDept());
                                    trade.setAftDept(BigDecimal.ZERO);
                                    break;
                                case "2":
                                    BigDecimal tranValue = dat.getTraAmount().divide(new BigDecimal("2"), 4, BigDecimal.ROUND_HALF_UP);
                                    trade.setPreDept(dat.getTraAmount());
                                    trade.setTraAmount(tranValue);
                                    trade.setAftDept(tranValue);
                                    break;
                            }
                            sumTranValue = sumTranValue.add(trade.getTraAmount());
                            String key = leveldbAccess.genCacheKey(oid, DsgAccountCard.class.getSimpleName(), dat.getCardNo());
                            DsgAccountCard dsgAccountCard = leveldbAccess.get(key);
                            if (dsgAccountCard != null) {
                                //结欠金額
                                dsgAccountCard.setArrearsValue(trade.getAftDept());
                                //待過數金額
                                dsgAccountCard.setWaitPostValue(dsgAccountCard.getWaitPostValue().add(trade.getTraAmount()));
                                dsgAccountCard.setTradeId(trade.getOid());
                                upDsgAccountCard.add(dsgAccountCard);
                            }
                            inDsgAccountTrade.add(trade);
                        }
                        DsgAccountTrade dat = dsgAccountTrades.get(0);
                        DsgBankDetail dbd = new DsgBankDetail();
                        dbd.setOid(detailOid);
                        dbd.setOilCom(dat.getOilCom());
                        dbd.setFlowNo(dat.getCusNo() + "-" + DsgUtils.getUUID());
                        dbd.setAccountNo(dat.getAccountNo());
                        dbd.setAccountBank(dat.getAccountName());
                        dbd.setBankCode(dat.getBankCode());
                        dbd.setBankName(dat.getBankName());
                        dbd.setPaymentName(dat.getPayer());
                        dbd.setTranTime(dat.getTranDate());
                        dbd.setStatus(DsgBankDetail.AppStatus.entered.getCode());
                        dbd.setTranValue(sumTranValue);
                        dbd.setPayMode(dat.getPayMode());
                        dsgBankDetailList.add(dbd);
                    }
                    //明細數據按收款帳號賬號分組
                    Map<String, List<DsgBankDetail>> dsgBankDetailMap = dsgBankDetailList.stream().collect(Collectors.groupingBy(I -> I.getAccountNo()));
                    for (String detailKey : dsgBankDetailMap.keySet()) {
                        List<DsgBankDetail> dsgBankDetails = dsgBankDetailMap.get(detailKey);
                        //銀行往來過數檔案
                        DsgBankRecord dsgBankRecord = new DsgBankRecord();
                        dsgBankRecord.setOid(UUIDGenerator.getUUID());
                        String accountBank = "", payMode = "";
                        //交易總金額
                        BigDecimal totalTranAmount = BigDecimal.ZERO;
                        //過數銀行賬戶
                        String bankNo = "";
                        for (DsgBankDetail detail : dsgBankDetails) {
                            detail.setRecordId(dsgBankRecord.getOid());
                            detail.setStatus(DsgBankDetail.AppStatus.generated.getCode());
                            if (StringUtil.isEmpty(accountBank)) {
                                accountBank = detail.getAccountBank();
                            }
                            if (StringUtil.isEmpty(payMode)) {
                                payMode = detail.getPayMode();
                            }
                            if (StringUtil.isEmpty(bankNo)) {
                                bankNo = detail.getBankCode();
                            }
                            //交易總金額
                            totalTranAmount = totalTranAmount.add(detail.getTranValue());
                        }
                        dsgBankRecord.setTotalTranAmount(totalTranAmount);
                        //交易總筆數
                        dsgBankRecord.setTotalTranNumber(dsgBankDetails.size());
                        dsgBankRecord.setSuccTranAmount(BigDecimal.ZERO);
                        dsgBankRecord.setSuccTranNumber(0);
                        dsgBankRecord.setFailTranAmount(BigDecimal.ZERO);
                        dsgBankRecord.setFailTranNumber(0);
                        dsgBankRecord.setStatus(DsgBankRecord.AppStatus.newGeneration.getCode());
                        dsgBankRecord.setDayOr(Integer.parseInt(dateType));
                        dsgBankRecord.setAccountBank(accountBank);
                        dsgBankRecord.setCountMode(getPayMode(payMode));
                        dsgBankRecord.setAccountNo(detailKey);
                        saveDsgBankRecordList.add(dsgBankRecord);
                        saveDsgBankDetailList.addAll(dsgBankDetails);
                    }
                    //保存銀行往來明細資料
                    saveBatch(saveDsgBankDetailList);
                    //保存銀行往來過數檔案
                    dsgBankRecordService.saveBatch(saveDsgBankRecordList);
                    //回填交易賬目
                    dsgAccountTradeService.saveBatch(inDsgAccountTrade);
                    //回填咭總賬
                    dsgAccountCardService.updateBatchById(upDsgAccountCard);
                }
            } finally {
                if (CollectionUtil.isNotEmpty(keyList)) {
                    keyList.forEach(I -> leveldbAccess.delete(I));
                }
            }
        } else {
            throw new RRException("上午/下午過數類型不能為空！");
        }
    }

    @Override
    public Page<DsgBankDetail> selectByDetailList(Page<DsgBankDetail> page, DsgBankDetail dsgBankDetail) {
        return page.setRecords(dsgBankDetailMapper.selectByDetailList(page, dsgBankDetail));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(String idList, Integer status) {
        QueryWrapper<DsgBankDetail> queryWrapper = new QueryWrapper();
        queryWrapper.in("OID", Arrays.asList(idList.split(",")));
        queryWrapper.notIn("STATUS", DsgBankDetail.AppStatus.success.getCode(), DsgBankDetail.AppStatus.failure.getCode());
        DsgBankDetail dsgBankDetail = new DsgBankDetail();
        dsgBankDetail.setStatus(status);
        dsgBankDetailMapper.update(dsgBankDetail, queryWrapper);
        //回填交易賬目和咭總賬
        List<DsgBankDetail> dsgBankDetailList = dsgBankDetailService.list(queryWrapper);
        if (CollectionUtil.isNotEmpty(dsgBankDetailList)) {
            DsgBankRecord dsgBankRecord = new DsgBankRecord();
            dsgBankRecord.setOid(dsgBankDetailList.get(0).getRecordId());
            dsgBankRecord.setStatus(status);
            dsgBankDetailService.bankCountingResults(dsgBankRecord, dsgBankDetailList);
        }

    }

    /**
     * 写交易账目
     *
     * @param arrearsValue        油咭總賬-结欠金額
     * @param inOut               收入/支出（IN/OUT）
     * @param dsgOilDataDetail    上油數據明細
     * @param amount              金额
     * @param dsgAccountTradeList 交易賬目集合
     * @param tranItem            交易專案
     * @param dsgBankDetail       銀行往來明細資料
     */
    private BigDecimal setDsgAccountTrade(BigDecimal arrearsValue, String inOut, DsgOilDataDetail dsgOilDataDetail,
                                          BigDecimal amount, List<DsgAccountTrade> dsgAccountTradeList,
                                          Integer tranItem, DsgBankDetail dsgBankDetail) {
        DsgAccountTrade dsgAccountTrade = new DsgAccountTrade();
        dsgAccountTrade.setOid(UUIDGenerator.getUUID());
        dsgAccountTrade.setOilCom(dsgOilDataDetail.getOilCom());
        dsgAccountTrade.setCusNo(dsgOilDataDetail.getCusNo());
        dsgAccountTrade.setCardNo(dsgOilDataDetail.getCardNo());
        //過數方式
        dsgAccountTrade.setCountMode(getPayMode(dsgOilDataDetail.getPayMode()));
        dsgAccountTrade.setInOut(inOut);
        //交易專案
        dsgAccountTrade.setTranItem(tranItem);
        //交易編碼
        dsgAccountTrade.setTranCode(SnowflakeKeyWorker.nextStrId());
        dsgAccountTrade.setTranDate(dsgOilDataDetail.getTransDate());
        dsgAccountTrade.setAccountName(dsgOilDataDetail.getAccountName());
        dsgAccountTrade.setAccountNo(dsgOilDataDetail.getAccountNo());
        dsgAccountTrade.setInDate(dsgOilDataDetail.getCreateTime());

        //承前結欠
        dsgAccountTrade.setPreDept(arrearsValue);
        dsgAccountTrade.setTraAmount(amount);
        //總結欠
        arrearsValue = arrearsValue.subtract(amount);
        dsgAccountTrade.setAftDept(arrearsValue);
        //交易帳目主鍵
        if (dsgBankDetail != null) {
            dsgBankDetail.setTranId(dsgAccountTrade.getOid());
        }
        dsgAccountTradeList.add(dsgAccountTrade);
        return arrearsValue;
    }

    /**
     * 取付款方式
     *
     * @param payMode
     * @return
     */
    private String getPayMode(String payMode) {
        if (StringUtil.isNotEmpty(payMode)) {
            switch (payMode) {
                case "1":
                    payMode = Constant.PAY_TYPE_CDC;
                    break;
                case "2":
                case "3":
                case "4":
                case "5":
                    payMode = Constant.PAY_TYPE_ATP;
                    break;
            }
            return payMode;
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bankCountingResults(DsgBankRecord dsgBankRecord, List<DsgBankDetail> list) {
        List<String> keyList = new ArrayList<>();
        try {
            String oid = UUIDGenerator.getUUID();
            SystemAuthorizingUser sysUser = SingletonLoginUtils.getSystemUser();
            //查詢銀行往來明細資料並緩存
            QueryWrapper<DsgBankDetail> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("RECORD_ID", dsgBankRecord.getOid());
            queryWrapper.eq("STATUS", DsgBankDetail.AppStatus.remitted.getCode());
            List<DsgBankDetail> dsgBankDetailList = dsgBankDetailService.list(queryWrapper);
            dsgBankDetailList.forEach(I -> {
                String key = leveldbAccess.genCacheKey(oid, DsgBankDetail.class.getSimpleName(), I.getFlowNo());
                leveldbAccess.put(key, I);
                keyList.add(key);
            });
            dsgBankDetailList.clear();
            //查詢交易賬目並緩存
            List<DsgAccountTrade> dsgAccountTradeList = dsgAccountTradeService.selectDsgAccountTradeListToRecordId(dsgBankRecord.getOid());
            Map<String, List<DsgAccountTrade>> dsgAccountTradeMap = dsgAccountTradeList.stream().collect(Collectors.groupingBy(I -> I.getBankDetailId()));
            for (String tradeKey : dsgAccountTradeMap.keySet()) {
                String key = leveldbAccess.genCacheKey(oid, DsgAccountTrade.class.getSimpleName(), tradeKey);
                leveldbAccess.put(key, dsgAccountTradeMap.get(tradeKey));
                keyList.add(key);
            }
            dsgAccountTradeList.clear();
            dsgAccountTradeMap.clear();
            //取油咭總賬并缓存
            QueryWrapper<DsgAccountCard> cardQueryWrapper = new QueryWrapper<>();
            cardQueryWrapper.eq("ENT_CODE", sysUser.getOrgCode());
            List<DsgAccountCard> dsgAccountCardList = dsgAccountCardMapper.selectList(cardQueryWrapper);
            dsgAccountCardList.forEach(I -> {
                String key = leveldbAccess.genCacheKey(oid, DsgAccountCard.class.getSimpleName(), I.getCardNo());
                leveldbAccess.put(key, I);
                keyList.add(key);
            });
            dsgAccountCardList.clear();
            //銀行往來過數結果
            List<DsgBankResult> dsgBankResultList = new ArrayList<>();
            //交易賬目
            List<DsgAccountTrade> inDsgAccountTradeList = new ArrayList<>();
            //油咭總賬
            List<DsgAccountCard> upDsgAccountCardList = new ArrayList<>();
            //总结欠金額
            BigDecimal arrearsValue = BigDecimal.ZERO;
            for (DsgBankDetail dsgBankDetail : list) {
                String key = leveldbAccess.genCacheKey(oid, DsgBankDetail.class.getSimpleName(), dsgBankDetail.getFlowNo());
                DsgBankDetail dbd = leveldbAccess.get(key);
                if (dbd != null) {
                    //銀行往來過數結果
                    DsgBankResult dsgBankResult = new DsgBankResult();
                    dsgBankResult.setOid(UUIDGenerator.getUUID());
                    dsgBankResult.setRecordId(dsgBankRecord.getOid());
                    dsgBankResult.setSerialNumber(dbd.getFlowNo());
                    dsgBankResult.setTransValue(dbd.getTranValue());
                    //過數方式
                    dsgBankResult.setCountMode(dsgBankRecord.getCountMode());
                    dsgBankResult.setBankName(dbd.getBankName());
                    dsgBankResult.setBankNo(dbd.getBankCode());
                    dsgBankResult.setHoldName(dbd.getPaymentName());
                    dsgBankResult.setCountDate(dsgBankDetail.getTranDate() == null ? null : DateUtil.parse(dsgBankDetail.getTranDate(), "DDMMYY"));
                    dsgBankResult.setResponseCode(dbd.getResponse());
                    switch (dsgBankDetail.getStatus()) {
                        case 3:
                            dsgBankResult.setResultStatus(DsgBankResult.AppStatus.success.getCode());
                            break;
                        case 4:
                            dsgBankResult.setResultStatus(DsgBankResult.AppStatus.failure.getCode());
                            break;
                    }
                    //交易賬目
                    key = leveldbAccess.genCacheKey(oid, DsgAccountTrade.class.getSimpleName(), dbd.getOid());
                    List<DsgAccountTrade> dsgAccountTrades = leveldbAccess.get(key);
                    if (CollectionUtil.isNotEmpty(dsgAccountTrades)) {
                        for (DsgAccountTrade dsgAccountTrade : dsgAccountTrades) {
                            //卡總帳
                            key = leveldbAccess.genCacheKey(oid, DsgAccountCard.class.getSimpleName(), dsgAccountTrade.getCardNo());
                            DsgAccountCard dsgAccountCard = leveldbAccess.get(key);
                            if (dsgAccountCard != null) {
                                arrearsValue = dsgAccountCard.getArrearsValue();
                                if (dsgBankDetail.getStatus().intValue() == 4) {
                                    dsgAccountTrade.setPreDept(arrearsValue);
                                    arrearsValue = arrearsValue.add(dsgAccountTrade.getTraAmount());
                                    dsgAccountTrade.setOid(UUIDGenerator.getUUID());
                                    dsgAccountTrade.setTraAmount(dsgAccountTrade.getTraAmount());
                                    dsgAccountTrade.setAftDept(arrearsValue);
                                    dsgAccountTrade.setBankDetailId(null);
                                    dsgAccountTrade.setCreater(null);
                                    dsgAccountTrade.setCreateTime(null);
                                    dsgAccountTrade.setUpdater(null);
                                    dsgAccountTrade.setUpdateTime(null);
                                    dsgAccountTrade.setEntCode(null);
                                    inDsgAccountTradeList.add(dsgAccountTrade);

                                    dsgAccountCard.setArrearsValue(arrearsValue);
                                }
                                dsgAccountCard.setTradeId(dsgAccountTrade.getOid());
                                dsgAccountCard.setWaitPostValue(dsgAccountCard.getWaitPostValue().subtract(dsgAccountTrade.getTraAmount()));
                                upDsgAccountCardList.add(dsgAccountCard);
                            }
                        }
                    }
                    dsgBankResultList.add(dsgBankResult);
                    dsgBankDetail.setOid(dbd.getOid());
                    dsgBankDetail.setResultId(dsgBankResult.getOid());
                }
            }
            //更新銀行往來明細資料
            dsgBankDetailService.updateBatchById(list);
            //回寫銀行往來過數結果
            dsgBankResultService.saveBatch(dsgBankResultList);
            //寫銀行返回失敗的交易賬目
            dsgAccountTradeService.saveBatch(inDsgAccountTradeList);
            //查詢判斷回填狀態
            queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("RECORD_ID", dsgBankRecord.getOid());
            queryWrapper.notIn("STATUS", DsgBankDetail.AppStatus.success.getCode(), DsgBankDetail.AppStatus.failure.getCode());
            if (dsgBankDetailService.count(queryWrapper) == 0) {
                DsgBankRecord dbr = dsgBankRecordService.getById(dsgBankRecord.getOid());
                dbr.setStatus(DsgBankRecord.AppStatus.complete.getCode());
                dsgBankRecordService.updateById(dbr);
            }
            //回填油咭總賬結欠總金額
            if (CollectionUtil.isNotEmpty(upDsgAccountCardList)) {
                for (DsgAccountCard dsgAccountCard : upDsgAccountCardList) {
                    if (dsgAccountCardMapper.updateById(dsgAccountCard) <= 0) {
                        throw new VersionException();
                    }
                }
            }
        } finally {
            if (CollectionUtil.isNotEmpty(keyList)) {
                keyList.forEach(I -> leveldbAccess.delete(I));
            }
        }
    }

    @Override
    public List<DsgAccountTrade> selectDsgAccountTradeList(String oid) {
        return dsgBankDetailMapper.selectDsgAccountTradeList(oid);
    }
}
